/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package filecopier.gui.scripteditor;

import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.beans.PropertyChangeSupport;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowEvent;
import javax.swing.JPanel;
import javax.swing.JDialog;
import javax.swing.JTabbedPane;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import filecopier.Singleton;
import filecopier.options.Options;
import filecopier.script.Script;
import filecopier.script.ScriptEntry;
import filecopier.script.CopyScript;
import filecopier.script.CopyScriptEntry;
import custom.swing.FileListPanel;
import custom.swing.GenericListModel;

/**
 *
 * @author david
 */
public class ScriptEditorPanel extends JPanel implements ActionListener, WindowListener {

    private JTabbedPane editorTabbedPane = null;
    private FileListPanel fileListPanel = null;
    private ScriptListPanel scriptListPanel = null;
    private Script script = null;
    private Options.CopyScriptType copyScriptType = null;
    private boolean scriptChanged = false;
    private FileFilter fileFilter = null;
    private JDialog dialog = null;
    private PropertyChangeSupport propertyChangeSupport = null;

    public ScriptEditorPanel(final JDialog dialog, final Options.CopyScriptType copyScriptType, final Script script) {
        super();
        this.script = script;
        this.copyScriptType = copyScriptType;
        this.dialog = dialog;
        propertyChangeSupport = new PropertyChangeSupport(this);
        initialize();
    }

    private void initialize() {
        this.getFileFilter();
        this.setLayout(new BorderLayout());
        this.add(getEditorTabbedPane(), java.awt.BorderLayout.CENTER);
    }

    public PropertyChangeSupport getPropertyChangeSupport() {
        return propertyChangeSupport;
    }

    private void setScriptChanged(boolean scriptChanged) {
        this.scriptChanged = scriptChanged;
        propertyChangeSupport.firePropertyChange("scriptChanged", !scriptChanged, scriptChanged);
    }

    private void setScriptName(final String oldName, final String newName) {
        propertyChangeSupport.firePropertyChange("scriptName", oldName, newName);
    }

    private JTabbedPane getEditorTabbedPane() {
        if (editorTabbedPane == null) {
            editorTabbedPane = new JTabbedPane();
            editorTabbedPane.addTab("Files", getFileListPanel());
            editorTabbedPane.addTab("Scripts", getScriptListPanel());
        }
        return editorTabbedPane;
    }

    private FileListPanel getFileListPanel() {
        if (fileListPanel == null) {
            fileListPanel = new FileListPanel(Singleton.getInstance().getFrame(),
                    getFileList(ScriptEntry.PathType.Normal),
                    (copyScriptType == Options.CopyScriptType.From ? FileListPanel.FILES_AND_DIRECTORIES : FileListPanel.DIRECTORIES_ONLY),
                    true);

            fileListPanel.getListModel().addListDataListener(new javax.swing.event.ListDataListener() {

                public void contentsChanged(javax.swing.event.ListDataEvent e) {
                    if (!scriptChanged) {
                        setScriptChanged(true);
                    }
                }

                public void intervalAdded(javax.swing.event.ListDataEvent e) {
                    if (!scriptChanged) {
                        setScriptChanged(true);
                    }
                }

                public void intervalRemoved(javax.swing.event.ListDataEvent e) {
                    if (!scriptChanged) {
                        setScriptChanged(true);
                    }
                }
            });

        }
        return fileListPanel;
    }

    private FileFilter getFileFilter() {
        if (fileFilter == null) {
            String ext = null;
            if (copyScriptType == Options.CopyScriptType.From) {
                ext = "fsc";
            } else if (copyScriptType == Options.CopyScriptType.To) {
                ext = "tsc";
            }

            fileFilter = new FileNameExtensionFilter("Copy Script Files", "csc", ext);
        }
        return fileFilter;
    }

    private ScriptListPanel getScriptListPanel() {
        if (scriptListPanel == null) {
            scriptListPanel = new ScriptListPanel(dialog, getFileList(ScriptEntry.PathType.Script), copyScriptType);
            scriptListPanel.setInitBrowseDir(new File(Singleton.getInstance().getOptions().getCopyPath().getPathArray()[0]));

            scriptListPanel.getListModel().addListDataListener(new javax.swing.event.ListDataListener() {

                public void contentsChanged(javax.swing.event.ListDataEvent e) {
                    if (!scriptChanged) {
                        setScriptChanged(true);
                    }
                }

                public void intervalAdded(javax.swing.event.ListDataEvent e) {
                    if (!scriptChanged) {
                        setScriptChanged(true);
                    }
                }

                public void intervalRemoved(javax.swing.event.ListDataEvent e) {
                    if (!scriptChanged) {
                        setScriptChanged(true);
                    }
                }
            });

        }
        return scriptListPanel;
    }

    private List<File> getFileList(ScriptEntry.PathType pathType) {
        List<File> fileList = null;

        if (script != null) {
            List<ScriptEntry> scriptEntries = script.getScriptEntries(pathType);
            fileList = new ArrayList<File>(scriptEntries.size());

            for (ScriptEntry se : scriptEntries) {
                fileList.add(se.getPath());
            }
        }

        return fileList;
    }

    private void handleNewBtn() {
        boolean status = true;

        if (scriptChanged == true) {
            status = promptToSave();
        }

        if (status == true) {
            fileListPanel.clearFileList();
            scriptListPanel.clearFileList();
            dialog.setTitle("Untitled");
            scriptChanged = false;
            repaint();
            try {
                script = new CopyScript();
            } catch (custom.xml.DOMHelperException e) {
                Singleton.getInstance().getFileLogger().error("DOMHelperException encountered", e);
            }
        }
    }

    private void handleOpenBtn() {
        JFileChooser fileChooser = null;
        boolean status = true;

        if (scriptChanged == true) {
            status = promptToSave();
        }

        if (status == true) {
            File scriptFile = null;
            if (script != null && (scriptFile = script.getScriptFile()) != null) {
                fileChooser = new JFileChooser(scriptFile.getPath());
            } else {
                fileChooser = new JFileChooser(Singleton.getInstance().getOptions().getCopyPath().getPathEntryValue(0));
            }

            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setFileFilter(fileFilter);

            if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
                try {
                    script = new CopyScript(fileChooser.getSelectedFile());

                    // Display the contents of the script file in the fileListPanel and the scriptListPanel.
                    GenericListModel<File> fileListModel = new GenericListModel<File>();
                    fileListModel.addAll(getFileList(ScriptEntry.PathType.Normal));
                    fileListPanel.setListModel(fileListModel);

                    GenericListModel<File> scriptListModel = new GenericListModel<File>();
                    scriptListModel.addAll(getFileList(ScriptEntry.PathType.Script));
                    scriptListPanel.setListModel(scriptListModel);

                    setScriptChanged(false);
                    dialog.setTitle(script.getScriptName());
                    setScriptName(scriptFile != null ? scriptFile.getPath() : null, script.getScriptName());
                    repaint();
                } catch (custom.xml.DOMHelperException e) {
                    javax.swing.JOptionPane.showMessageDialog(dialog, "\"" + fileChooser.getSelectedFile() + "\" is in an invalid format.",
                            "Invalid Script File", JOptionPane.ERROR_MESSAGE);

                    Singleton.getInstance().getFileLogger().error("DOMHelperException encountered", e);
                } catch (java.io.IOException e) {
                    javax.swing.JOptionPane.showMessageDialog(dialog, "\"" + fileChooser.getSelectedFile() + "\" is in an invalid format.",
                            "Invalid Script File", JOptionPane.ERROR_MESSAGE);

                    Singleton.getInstance().getFileLogger().error("IOException encountered", e);
                }
            }
        }
    }

    private void handleSaveBtn() {
        if (script == null || script.getScriptName() == null) {
            handleSaveAsBtn();
        } else {
            try {
                saveScriptFile(script.getScriptName(), false);
            } catch (custom.xml.DOMHelperException e) {
                // Bring up a Message box that shows the file name entered to be invalid.
                Singleton.getInstance().getFileLogger().error("DOMHelperException encountered", e);
            } catch (java.io.IOException e) {
                // Bring up a Message box that shows the file name entered to be invalid.
                Singleton.getInstance().getFileLogger().error("IOException encountered", e);
            }
        }
    }

    private void handleSaveAsBtn() {
        boolean status = false;

        // If script is not null, set the file chooser to open in the directory of the script file. If it is null, open in the first path
        // in the copy path.
        JFileChooser fileChooser = new JFileChooser(script != null ? script.getScriptFile()
                : new File(Singleton.getInstance().getOptions().getCopyPath().getPathEntryValue(0)));

        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fileChooser.setFileFilter(fileFilter);
        // If script is not null, set the selected file to the script file.
        if (script != null) {
            fileChooser.setSelectedFile(script.getScriptFile());
        }
        if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            String newScriptName = fileChooser.getSelectedFile().getPath();

            try {
                boolean nameChanged = script != null && script.getScriptName() != null ? (!script.getScriptName().equalsIgnoreCase(newScriptName) ? true : false) : true;
                String oldScriptName = null;
                if (nameChanged) {
                    oldScriptName = dialog.getTitle();
                    // Make sure script name ends with a .csc extension.
                    if(!newScriptName.endsWith(".csc")) {
                        newScriptName += ".csc";
                    }
                    dialog.setTitle(newScriptName);
                }
                saveScriptFile(newScriptName, false);
                if (oldScriptName != null) {
                    setScriptName(oldScriptName, newScriptName);
                }
            } catch (custom.xml.DOMHelperException e) {
                // Bring up a Message box that shows the file name entered to be invalid.
                Singleton.getInstance().getFileLogger().error("DOMHelperException encountered", e);
            } catch (java.io.IOException e) {
                // Bring up a Message box that shows the file name entered to be invalid.
                Singleton.getInstance().getFileLogger().error("IOException encountered", e);
            }
        }
    }

    private void handleSaveAsTempBtn() {
        String tempNm = generateTempScriptFileNm();
        try {
            saveScriptFile(tempNm, true);
            setScriptName(dialog.getTitle(), tempNm);
            dialog.setTitle(tempNm);
        } catch (custom.xml.DOMHelperException e) {
            // Bring up a Message box that shows the file name entered to be invalid.
            Singleton.getInstance().getFileLogger().error("DOMHelperException encountered", e);
        } catch (java.io.IOException e) {
            // Bring up a Message box that shows the file name entered to be invalid.
            Singleton.getInstance().getFileLogger().error("IOException encountered", e);
        }
    }

    private void handleDeleteBtn() {
        if ((script.getScriptName() != null) && (!dialog.getTitle().equals("Untitled")) && (promptToDelete())) {
            script.getScriptFile().delete();
            dialog.setTitle("Untitled");
            setScriptChanged(false);
            // Treat this as if the new button were pressed.
            handleNewBtn();
        }
    }

    private void handleCloseBtn() {
        boolean status = scriptChanged == true ? promptToSave() : true;
        if (status) {
            dialog.dispose();
        }
    }

    private void saveScriptFile(final String scriptName, final boolean isTempScript) throws custom.xml.DOMHelperException, java.io.IOException {
        ScriptEntry[] scriptEntries = getAllScriptEntries();
        if (scriptEntries != null) {

            script = new CopyScript();
            script.addScriptEntries(scriptEntries);
            script.saveToDisk(new File(scriptName), isTempScript);
        }
        setScriptChanged(false);
    }

    private ScriptEntry[] getScriptEntries(ScriptEntry.PathType pathType, GenericListModel<File> listModel) {
        ScriptEntry[] scriptEntries = null;
        int size = listModel.getSize();

        if (listModel != null && size > 0) {
            scriptEntries = new CopyScriptEntry[size];
            for (int i = 0; i < size; i++) {
                scriptEntries[i] = new CopyScriptEntry(pathType, listModel.get(i).getAbsoluteFile());
            }
        }

        return scriptEntries;
    }

    private ScriptEntry[] getAllScriptEntries() {
        ScriptEntry[] allScriptEntries = null;

        // Delete any empty entries that are in the list panels.
        fileListPanel.deleteEmptyEntries();
        scriptListPanel.deleteEmptyEntries();

        GenericListModel<File> listModel = (GenericListModel<File>) fileListPanel.getListModel();
        ScriptEntry[] normalScriptEntries = getScriptEntries(ScriptEntry.PathType.Normal, listModel);

        listModel = (GenericListModel<File>) scriptListPanel.getListModel();
        ScriptEntry[] scriptScriptEntries = getScriptEntries(ScriptEntry.PathType.Script, listModel);

        if (normalScriptEntries != null && scriptScriptEntries != null) {
            allScriptEntries = new ScriptEntry[normalScriptEntries.length + scriptScriptEntries.length];
            int i;
            for (i = 0; i < normalScriptEntries.length; i++) {
                allScriptEntries[i] = normalScriptEntries[i];
            }
            for (int j = 0; j < scriptScriptEntries.length; j++, i++) {
                allScriptEntries[i] = scriptScriptEntries[j];
            }
        } else if (normalScriptEntries != null) {
            allScriptEntries = normalScriptEntries;
        } else if (scriptScriptEntries != null) {
            allScriptEntries = scriptScriptEntries;
        }

        return allScriptEntries;
    }

    private String generateTempScriptFileNm() {
        String tempDirNm = System.getProperty("filecopier.scripts.temp");
        String tempPathNm = null;

        do {
            String tempFileName = "Temp" + Integer.toString(new java.util.Random().nextInt() & 0xffff) + ".csc";
            tempPathNm = tempDirNm + System.getProperty("file.separator") + tempFileName;
        } while (new File(tempPathNm).exists());

        return tempPathNm;
    }

    private boolean promptToSave() {
        boolean status = true;

        int result = JOptionPane.showConfirmDialog(dialog,
                "The text in the " + dialog.getTitle() + " file has changed.",
                "Do you want to save the changes?",
                JOptionPane.YES_NO_CANCEL_OPTION,
                JOptionPane.WARNING_MESSAGE);

        if (result == JOptionPane.OK_OPTION) {
            handleSaveBtn();
        } else if (result == JOptionPane.CANCEL_OPTION) {
            status = false;
        }

        return status;
    }

    private boolean promptToDelete() {
        boolean status = false;

        int result = JOptionPane.showConfirmDialog(dialog, "Are you sure you want to delete the file " + dialog.getTitle() + "?",
                "This file has been marked for deletion.",
                JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);

        if (result == JOptionPane.YES_OPTION) {
            status = true;
        }

        return status;
    }

    public void actionPerformed(ActionEvent ae) {
        if (ae.getSource() instanceof javax.swing.JButton) {
            String actionCommand = ae.getActionCommand();

            if (actionCommand.equals("New")) {
                handleNewBtn();
            } else if (actionCommand.equals("Open")) {
                handleOpenBtn();
            } else if (actionCommand.equals("Save")) {
                handleSaveBtn();
            } else if (actionCommand.equals("Save As")) {
                handleSaveAsBtn();
            } else if (actionCommand.equals("Save As Temp")) {
                handleSaveAsTempBtn();
            } else if (actionCommand.equals("Delete")) {
                handleDeleteBtn();
            } else if (actionCommand.equals("Close")) {
                handleCloseBtn();
            }
        }
    }

    public void windowActivated(WindowEvent e) {
    }

    public void windowClosed(WindowEvent e) {
    }

    public void windowDeactivated(WindowEvent e) {
    }

    public void windowDeiconified(WindowEvent e) {
    }

    public void windowIconified(WindowEvent e) {
    }

    public void windowOpened(WindowEvent e) {
    }

    public void windowClosing(WindowEvent e) {
        handleCloseBtn();
    }
}
